package sample;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Random;

import org.jfree.chart.entity.StandardEntityCollection;

import rescuecore2.messages.Command;
import rescuecore2.standard.entities.AmbulanceTeam;
import rescuecore2.standard.entities.Civilian;
import rescuecore2.standard.entities.FireBrigade;
import rescuecore2.standard.entities.Human;
import rescuecore2.standard.entities.PoliceOffice;
import rescuecore2.standard.entities.Refuge;
import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.standard.entities.StandardEntityURN;
import rescuecore2.standard.messages.AKSpeak;
import rescuecore2.worldmodel.ChangeSet;
import rescuecore2.worldmodel.EntityID;
import rescuecore2.standard.entities.PoliceForce;


/**
   A sample ambulance team agent.
 */
public class SampleAmbulanceTeam extends AbstractSampleAgent<AmbulanceTeam> {
    private static final String MAX_VIEW_KEY = "perception.los.max-distance";
    private Collection<EntityID> unexploredBuildings;
    private int Estado;
    private int lastPos = -1;
    private int tentativas;
    private int maxView;
    private Boolean mudarRota = false;
    private List<EntityID> Destination = new ArrayList<EntityID>();
//    private List<Building> Incendios = new ArrayList<Building>();
    private List<StandardEntity> Vitimas = new ArrayList<StandardEntity>();
    private List<StandardEntity> VitimasSalvas = new ArrayList<StandardEntity>();
    private List<StandardEntity> VitimasSalvasEnviar = new ArrayList<StandardEntity>();
    private List<StandardEntity> VitimasVisao = new ArrayList<StandardEntity>();
    
    @Override
    public String toString() {
        return "Sample ambulance team";
    }

    @Override
    protected void postConnect() {
        super.postConnect();
        maxView = config.getIntValue(MAX_VIEW_KEY);
        model.indexClass(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM, StandardEntityURN.REFUGE, StandardEntityURN.BUILDING);
        unexploredBuildings = new HashSet<EntityID>(buildingIDs);
    }

    @Override
    protected void think(int time, ChangeSet changed, Collection<Command> heard) {
        if (time == config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY)) {
            // Subscribe to channel 1
            sendSubscribe(time, CANAL_AMBULANCIAS);
        }
        for (Command next : heard) {
            ////System.out.println("Heard " + next);
        }
        
        updateUnexploredBuildings(changed);
        
        AmbulanceTeam me = me();
        
        //1 - Olhar o ambiente e procurar vítimas no campo de visao
        ////System.out.println(me.getID() + " - Percebendo Vítimas");
        perceberVitimas();
        
        //2 - Enviar mensagens
        ////System.out.println(me.getID() + " - Enviando mensagens");
        enviarMensagem(time);
        
        //3 - Receber as mensagens e atualizar as vítimas
        ////System.out.println(me.getID() + " - Recebendo mensagens");
        receberMensagem(heard);

        /*
         * Sequencia de Ações:
         * 1 - Carregando um civil e está no refúgio
         * 2 - Carregando alguém e não está no refúgio
         * 3 - Tem civis na lista de vítimas, indo buscar um civil
         * 4 - Não tem civis na lista, procurando por vítimas
         */
        
        if (someoneOnBoard() && location() instanceof Refuge) {
        	Estado = 1;
        }else if (someoneOnBoard()){
        	Estado = 2;
        }else if (Vitimas.size() > 0){
        	Estado = 3;
        }else{
        	Estado = 4;
        }
        System.out.println("AMBULANCIA " + me.getID() + " - " + Estado);
    	////System.out.println(me.getID() + " ESTADO: " + Estado);
    	switch (Estado){
    	case 1:
    		////System.out.println(me.getID() + "Descarregando");
    		sendUnload(time);
            return;
    	case 2:
    		////System.out.println(me.getID() + "Indo para o Refugio");
        	gotoRefugio(time);
        	return;
    	case 3:
    		gotoVitima(time);
    		return;
    	case 4:
			////System.out.println(me().getID() + " - Estou procurando por vítimas.");
			procurarVitimas(time);
			return;
    	}
    }
    
    private void gotoVitima(int time){
    	////System.out.println(me().getID() + " - GOTOVITIMA");
    	
    	if(Vitimas.size() > 1)
    		OrdenaVitimas();
    	
    	System.out.println("DEPOIS DE ORDENAR");
    	for (StandardEntity h : Vitimas) {
    		Human c = (Human)h;
    		int i = -1;
    		System.out.println(i++ + " - " + c.getID() + " - " + c.getPosition() + " - " + c.getClass());
    	}

    	////System.out.println(me().getID() + " - Vitimas.size: " + Vitimas.size());
        for (StandardEntity h : Vitimas) {
        	Human next = (Human)h;
        	
        	////System.out.println(me().getID() + " VitimasVisao.size: " + VitimasVisao.size());
        	for (StandardEntity c : VitimasVisao) {
        		////System.out.println(me().getID() + " - Visao: " + c.getID());
        	}
        	
        	////System.out.println(me().getID() + "next.getPosition().getValue() == me().getPosition().getValue(): " +  next.getPosition().getValue() + " --- " +  me().getPosition().getValue());
        	////System.out.println(me().getID() + "!VitimasVisao.contains(next.getID()): " + !VitimasVisao.contains(next.getID()));
        	
        	if (next.getPosition().getValue() == me().getPosition().getValue() && !VitimasVisao.contains(next)){
        		VitimasSalvas.add(next);
        		procurarVitimas(time);
        	}
        	
        	////System.out.println(me().getID() + " next.getPosition().getValue() == me().getPosition().getValue(): " + next.getPosition().getValue() + " --- " + me().getPosition().getValue());
        	////System.out.println(me().getID() + " VitimasVisao.contains(next): " + VitimasVisao.contains(next));
        	
            if (next.getPosition().getValue() == me().getPosition().getValue() && VitimasVisao.contains(next)) {
            	////System.out.println(me().getID() + " na mesma posição que a vitima");
            	
            	////System.out.println(me().getID() + " next instanceof Civilian: " + (next instanceof Civilian));
            	////System.out.println(me().getID() + " next.getBuriedness(): " + next.getBuriedness());
            	////System.out.println(me().getID() + " !(location() instanceof Refuge): " + !(location() instanceof Refuge));
                if ((next instanceof Civilian) && next.getBuriedness() == 0 && !(location() instanceof Refuge)) {
                    // Load
                	////System.out.println("Loading " + next);
                	VitimasSalvas.add(next);
                	VitimasSalvasEnviar.add(next);
                    sendLoad(time, next.getID());
                    return;
                }
                if (next.getBuriedness() > 0) {
                    // Rescue
                	////System.out.println("Rescueing " + next);
                    sendRescue(time, next.getID());
                    return;
                }
            }else {
            	////System.out.println(me().getID() + " - MOVETOTARGET");

            	////System.out.println(me().getID() + " next.getPosition().getValue() == me().getPosition().getValue(): " + next.getPosition().getValue() + " --- " + me().getPosition().getValue());
            	if(next.getPosition().getValue() == me().getPosition().getValue()){
            		VitimasSalvas.add(next);
            		procurarVitimas(time);
            	}
            	
            	Destination = plantoDestination(Destination.get(Destination.size()-1));
                if (Destination != null) {
                	////System.out.println("Moving to target");
                    MoveTo(time, Destination);
                    return;
                }
            }
        }
    }
    
    private void gotoRefugio(int time){
		//Procura o caminho até o refúgio
		Destination = search.breadthFirstSearch(me().getPosition(), refugeIDs);
		
		//Caso não encontre o caminho, aguarda até que a via esteja liberada para então continuar o caminho
		if (Destination != null) {
			//System.out.println(me().getID() + " - Indo para " + Destination.get(Destination.size()-1));
        	MoveTo(time, Destination);
        } else {
            //System.out.println(me().getID() + " - Não encontrou o caminho até o refúgio, aguardando liberação da via.");
            sendRest(time);
        }
    }
    
    private void MoveTo(int time, List<EntityID> Dest){
        lastPos = me().getPosition().getValue();
        mudarRota = false;
    	sendMove(time, Destination);
    	return;
    }

    private void procurarVitimas(int time){
    	//System.out.println(me().getID() + " - PROCURARVITIMAS");
    	
    	//Está andando aleatoriamente e ainda não chegou a seu destino
        //System.out.println("AMBULANCIA " + me().getID() + " - " + Destination);
    	if(Destination != null && Destination.size() > 0){
    		if (Destination.get(Destination.size()-1) != me().getPosition() && !mudarRota){
        		Destination = plantoDestination(Destination.get(Destination.size()-1));
        		MoveTo(time, Destination);
            	if (lastPos == me().getPosition().getValue()){
            		mudarRota = true;
            	}	
    		} else {
    			gotoAleatorio(time);
    		}
    	} else {
    		gotoAleatorio(time);
    	}
    }
    
    private void gotoAleatorio(int time){
		//Está andando aleatoriamente e já chegou a seu destino
		Destination = randomWalk();
		tentativas = 0;
		//System.out.println(me().getID() + " - Random destination");
		while (Destination == null && tentativas < 3){
			//System.out.println(me().getID() + " - Não encontrou um caminho válido, fazendo mais uma tentativa(" + tentativas + ")");
			tentativas++;
			Destination = randomWalk();
		}
		
        //System.out.println("AMBULANCIA " + me().getID() + " - " + Destination);
		//Caso não encontre o caminho, aguarda até que a via esteja liberada para então continuar o caminho
		if (Destination != null) {
			//System.out.println(me().getID() + " - Indo para " + Destination.get(Destination.size()-1));
        	MoveTo(time, Destination);
        } else {
            //System.out.println(me().getID() + " - Não encontrou nenhum caminho válido, aguardando liberação da via.");
            sendRest(time);
        }
    }

    @Override
    protected List<EntityID> randomWalk() {
    	return search.breadthFirstSearch(me().getPosition(), buildingIDs.get(new Random().nextInt(buildingIDs.size())));
    }
    
    protected List<EntityID> plantoDestination(EntityID e) {
    	return search.breadthFirstSearch(me().getPosition(), e);
    }

    private void perceberVitimas(){
    	//System.out.println(me().getID() + " - PERCEBERVITIMAS");
    	//Identifica todos os civis no campo de visão
        Collection<StandardEntity> all = getCivilians();
        
        if (Vitimas.size() > 0 ){
        	//System.out.println(me().getID() + " - As seguintes vítimas são conhecidas:");
        	for (StandardEntity h : Vitimas){
        		//System.out.println(me().getID() + " - Vítima: " + h.getID());
        	}
        }
        
    	//Verifica se eles já não então inseridos na lista de vítimas
    	for(StandardEntity e : all){
    		if (e instanceof Human){
        		//System.out.println(me().getID() + " - Identificou a vítima " + e.getID());
        		if(!Vitimas.contains(e)){
        			Vitimas.add(e);
        		}
    		}
    	}
    	
    	//System.out.println(me().getID() + " - Tem isso nas vítimas:");
    	for(StandardEntity b : Vitimas){
    		//System.out.println(me().getID() + " - Vítima: " + b.getID());
    	}
    	
        Collection<StandardEntity> all2 = getAllCivilians();
        //System.out.println(me().getID() + " - VÍTIMAS NO CAMPO DE VISÃO:");
        VitimasVisao.clear();
    	for(StandardEntity e : all2){
    		if (e instanceof Human){
        		//System.out.println(me().getID() + " - Identificou a vítima " + e.getID());
        		if(!VitimasVisao.contains(e)){
        			VitimasVisao.add(e);
        		}
    		}
    	}
    }

    private Collection<StandardEntity> getCivilians() {
        List<StandardEntity> result = new ArrayList<StandardEntity>();
        for (StandardEntity next : model.getEntitiesOfType(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM)) {
            if (next instanceof Civilian && next != null) {
            	Human h = (Human)next;
                
                if (h.isHPDefined()
                    && h.isBuriednessDefined()
                    && h.isDamageDefined()
                    && h.isPositionDefined()
                    && h.getHP() > 0
                    && (h.getBuriedness() > 0 || h.getDamage() > 0)) {
                    result.add(h);
                }
            }
        }
        return result;
    }
    
    private Collection<StandardEntity> getAllCivilians() {
        List<StandardEntity> result = new ArrayList<StandardEntity>();
        Collection<StandardEntity> e = model.getEntitiesOfType(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM);
    	//System.out.println("GETALLCIVILIANS: " + e.size());
    	
        for (StandardEntity next : e) {
            if (next instanceof Civilian && next != null) {
            	Human h = (Human)next;
            	//System.out.println("GETALLCIVILIANS - " + h.getID() + " - " + model.getDistance(me().getPosition(), h.getPosition()) + " - " + maxView);
            	
                if (h.isHPDefined()
                    && h.isBuriednessDefined()
                    && h.isDamageDefined()
                    && h.isPositionDefined()
                    && h.getHP() > 0) {
                    result.add(h);
                }
            }
        }
        return result;
    }
    
    @Override
    protected EnumSet<StandardEntityURN> getRequestedEntityURNsEnum() {
        return EnumSet.of(StandardEntityURN.AMBULANCE_TEAM);
    }

    private boolean someoneOnBoard() {
        for (StandardEntity next : model.getEntitiesOfType(StandardEntityURN.CIVILIAN)) {
            if (((Human)next).getPosition().equals(getID())) {
            	//System.out.println(next + " is on board");
                return true;
            }
        }
        return false;
    }

    private List<Human> getTargets() {
        List<Human> targets = new ArrayList<Human>();
        for (StandardEntity next : model.getEntitiesOfType(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM)) {
            Human h = (Human)next;
            if (h == me()) {
                continue;
            }
            if (h.isHPDefined()
                && h.isBuriednessDefined()
                && h.isDamageDefined()
                && h.isPositionDefined()
                && h.getHP() > 0
                && (h.getBuriedness() > 0 || h.getDamage() > 0)) {
                targets.add(h);
            }
        }
        Collections.sort(targets, new DistanceSorter(location(), model));
        return targets;
    }

    private void updateUnexploredBuildings(ChangeSet changed) {
        for (EntityID next : changed.getChangedEntities()) {
            unexploredBuildings.remove(next);
        }
    }
    
    private void enviarMensagem(int time){
        String Mensagem = "";
        String tipo = "";
        
        if (Vitimas.size() > 0){
        	 Mensagem = "#";
             for (StandardEntity b : Vitimas) {
            	 if(b instanceof Civilian){
            		tipo = "C"; 
            	 }else if (b instanceof FireBrigade){
            		 tipo = "F";
            	 }else if (b instanceof PoliceOffice){
            		 tipo = "P";
            	 }else if (b instanceof AmbulanceTeam){
            		 tipo = "A";
            	 }
        		 Human h = (Human)b;
        		 Mensagem += "V" + tipo + h.getID() + "B" + h.getBuriedness() + "H" + h.getHP() + "D" + h.getDamage() + "P" + h.getPosition();
             }
        }
        if (VitimasSalvasEnviar.size() > 0){
        	if (!Mensagem.contains("#"))
        		Mensagem = "#";
        	for (StandardEntity b : VitimasSalvasEnviar) {
        		if(b instanceof Civilian){
        			tipo = "C"; 
        		}else if (b instanceof FireBrigade){
        			tipo = "F";
        		}else if (b instanceof PoliceOffice){
        			tipo = "P";
        		}else if (b instanceof AmbulanceTeam){
        			tipo = "A";
        		}
        		Human h = (Human)b;
        		Mensagem += "S" + tipo + h.getID();
        	}
        	VitimasSalvasEnviar.clear();
        }

        
        if(Mensagem.trim().length() > 0){
//            //System.out.println(me().getID() + " - Enviando a mensagem: " + Mensagem);            
            sendSpeak(time, CANAL_AMBULANCIAS, Mensagem.getBytes());        	
        }
    }
    
    private void receberMensagem(final Collection<Command> heard){

    	
        //Envia as mensagens dos incendios que reconhece e dos civis soterrados
    	for (final Command next : heard) {
            if (next instanceof AKSpeak) {
            	final String aux = new String(((AKSpeak) next).getContent());
//        		//System.out.println(me().getID() + " - RECEBIDO - " + aux);            	
            	if (aux.length() > 0){ 
            		if(aux.contains("#")){
//                		//System.out.println("1 - " + aux);
            			String[] aux2 = aux.split("#");
            			for(String s : aux2){
            				if(s.contains("V")){
            					String[] aux3 = s.split("V");
            					for(String n : aux3){
            						if (n.contains("S")){
            							String[] aux4 = n.split("S");
            							for(String n2 : aux4){
            								if(n.contains("V"))
            									AdicionarVitima(n2);
            								else
            									AdicionarVitimaSalva(n2);
            							}
            						}
            						else
            							AdicionarVitima(n);
            					}
            				}
            			}
            		}
            	}
            }
    	}
    }
    
    private void AdicionarVitima(String n){
    	Boolean tem = false;
    	int ID = 0;
    	String tipo = "";
    	
    	//System.out.println(me().getID() + " - 2 - " + n);
		if (!n.equals("")){
			try{
				tipo = n.substring(0, 1);
				ID = Integer.parseInt(n.substring(1,n.indexOf("B")));
//        		//System.out.println(me().getID() + " - 2 - Adicionou: " + n);
        		
        		if (tipo.equals("C")){
        			Civilian h = new Civilian(new EntityID(ID));
        			if (!Vitimas.contains(h) && !VitimasSalvas.contains(h)){
        				h.setBuriedness(Integer.parseInt(n.substring(n.indexOf("B")+1,n.indexOf("H"))));
        				h.setHP(Integer.parseInt(n.substring(n.indexOf("H")+1,n.indexOf("D"))));
        				h.setDamage(Integer.parseInt(n.substring(n.indexOf("D")+1,n.indexOf("P"))));
        				h.setPosition(new EntityID(Integer.parseInt(n.substring(n.indexOf("P")+1))));
        				Vitimas.add(h);
        			}
            			
        		}else if (tipo.equals("F")){
        			FireBrigade h = new FireBrigade(new EntityID(ID));
        			if (!Vitimas.contains(h) && !VitimasSalvas.contains(h)){
        				h.setBuriedness(Integer.parseInt(n.substring(n.indexOf("B")+1,n.indexOf("H"))));
        				h.setHP(Integer.parseInt(n.substring(n.indexOf("H")+1,n.indexOf("D"))));
        				h.setDamage(Integer.parseInt(n.substring(n.indexOf("D")+1,n.indexOf("P"))));
        				h.setPosition(new EntityID(Integer.parseInt(n.substring(n.indexOf("P")+1))));
            			Vitimas.add(h);
        			}
        		}else if (tipo.equals("P")){
        			PoliceForce h = new PoliceForce(new EntityID(ID));
        			if (!Vitimas.contains(h) && !VitimasSalvas.contains(h)){
        				h.setBuriedness(Integer.parseInt(n.substring(n.indexOf("B")+1,n.indexOf("H"))));
        				h.setHP(Integer.parseInt(n.substring(n.indexOf("H")+1,n.indexOf("D"))));
        				h.setDamage(Integer.parseInt(n.substring(n.indexOf("D")+1,n.indexOf("P"))));
        				h.setPosition(new EntityID(Integer.parseInt(n.substring(n.indexOf("P")+1))));
            			Vitimas.add(h);
        			}
        		}else if (tipo.equals("A")){
        			AmbulanceTeam h = new AmbulanceTeam(new EntityID(ID));
        			if (!Vitimas.contains(h) && !VitimasSalvas.contains(h)){
        				h.setBuriedness(Integer.parseInt(n.substring(n.indexOf("B")+1,n.indexOf("H"))));
        				h.setHP(Integer.parseInt(n.substring(n.indexOf("H")+1,n.indexOf("D"))));
        				h.setDamage(Integer.parseInt(n.substring(n.indexOf("D")+1,n.indexOf("P"))));
        				h.setPosition(new EntityID(Integer.parseInt(n.substring(n.indexOf("P")+1))));
            			Vitimas.add(h);
        			}
        		}else{
//        			//System.out.println("DEU ERROOOOOOOOOOOOOOOOOOOOOOOOOOO");
        		}
        		
				tem = true;
			}catch(Exception e){
//				//System.out.println(me().getID() + " - Não deve ter nada antes do V." + n);
				tem = false;
			}
		}
    }
    
    private void AdicionarVitimaSalva(String n){
    	Boolean tem = false;
    	int ID = 0;
    	String tipo = "";
    	
//    	//System.out.println(me().getID() + " - 2 - " + n);
		if (!n.equals("")){
			try{
				tipo = n.substring(0, 1);
				ID = Integer.parseInt(n.substring(1));
//        		//System.out.println(me().getID() + " - 2 - Adicionou: " + n);
        		
        		if (tipo.equals("C")){
        			Civilian h = new Civilian(new EntityID(ID));
        			Vitimas.remove(h);
        			if (!VitimasSalvas.contains(h)){
        				VitimasSalvas.add(h);
        			}
            			
        		}else if (tipo.equals("F")){
        			FireBrigade h = new FireBrigade(new EntityID(ID));
        			Vitimas.remove(h);
        			if (!VitimasSalvas.contains(h)){
        				VitimasSalvas.add(h);
        			}
        		}else if (tipo.equals("P")){
        			PoliceForce h = new PoliceForce(new EntityID(ID));
        			Vitimas.remove(h);
        			if (!VitimasSalvas.contains(h)){
        				VitimasSalvas.add(h);
        			}
        		}else if (tipo.equals("A")){
        			AmbulanceTeam h = new AmbulanceTeam(new EntityID(ID));
        			Vitimas.remove(h);
        			if (!VitimasSalvas.contains(h)){
        				VitimasSalvas.add(h);
        			}
        		}else{
//        			//System.out.println("DEU ERROOOOOOOOOOOOOOOOOOOOOOOOOOO");
        		}
        		
				tem = true;
			}catch(Exception e){
//				//System.out.println(me().getID() + " - Não deve ter nada antes do V." + n);
				tem = false;
			}
		}
    }
    
    private void OrdenaVitimas(){
    	 Collections.sort(Vitimas, new Comparator() {  
             public int compare(Object o1, Object o2) {  
                 Human h1 = (Human) o1;  
                 Human h2 = (Human) o2;
                 System.out.println(h1.getID() + ": " + CalculaIndice(h1) + " " + h2.getID() + ": " + CalculaIndice(h2));
                 return CalculaIndice(h1) < CalculaIndice(h2) ? -1 : (CalculaIndice(h1) > CalculaIndice(h1) ? +1 : 0);  
             }  
         }); 
    }
    
    private double CalculaIndice(Human h){
    	Double Hp = 0.7;
    	Double Dm = 0.9;
    	Double Br = 0.5;
    	Double Ds = 0.6;
    	
    	return (Hp * h.getHP()) + (Br * h.getBuriedness()) + (Dm * h.getDamage()) + (Ds * model.getDistance(me().getPosition(), h.getPosition()));
    }
}
